Fork me on GitHub

HandlerAdapter

注意:所有文章除特别说明外,转载请注明出处.

第13章 HandlerAdapter

[TOC]

HandlerMapping通过request找到Handler,而HandlerAdapter是具体使用Handler来干活的。每个HandlerAdapter封装了一种Handler的具体使用方法。HandlerAdapter结构中有5个Adapter,其中只有RequestMappingHandlerAdapter有两层,而其它的只有一层,是直接实现HandlerAdapter接口。同时其中的有一个类已经弃用,所以只有4类Adapter,5个类。

提示:在HandlerAdapter的接口中只有三个方法:1.判断是否支持传入Handler。2.使用Handler处理请求。3.获取资源的LastModified值。

13.1 RequestMappingHandlerAdapter

RequestMappingHandlerAdapter适配器是与RequestMappingHandlerMapping配合使用的。

13.1.1 AbstractHandlerMethodAdapter

1.supports()方法

AbstractHandlerMethodAdapter类实现了supports()方法,可以知道支持的Handler是HandlerMethod,同时附加了一个判断方法supportsInternal,这个方法由RequestMappingHandlerAdapter实现,其就是简单地返回true。

@Override
public final boolean supports(Object handler) {
    // 判断当前handler是否为HandlerMethod类型,并且判断supportsInternal()方法返回值是否为true,
    // 这里supportsInternal()方法是提供给子类实现的一个方法,对于RequestMappingHandlerAdapter
    // 而言,其返回值始终是true,因为其只需要处理的handler是HandlerMethod类型的即可
    return (handler instanceof HandlerMethod 
            && supportsInternal((HandlerMethod) handler));
}

@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    // 这里RequestMappingHandlerAdapter只是对supportsInternal()返回true,因为其只需要
    // 处理的handler类型是HandlerMethod类型即可
    return true;
}

@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    // 这里RequestMappingHandlerAdapter只是对supportsInternal()返回true,因为其只需要
    // 处理的handler类型是HandlerMethod类型即可
    return true;
}

2.handle()方法

在supports()方法判断了所处理的handler是HandlerMethod类型之后,RequestMappingHandlerAdapter将会调用handle()方法处理当前请求。AbstractHandlerMethodAdapter实现了handle方法,就是简单地调用了模板方法handleInternal,handleInternal方法由RequestMappingHandlerAdapter实现。

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    checkRequest(request);

    // 判断当前是否需要支持在同一个session中只能线性地处理请求
    if (this.synchronizeOnSession) {
        // 获取当前请求的session对象
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 为当前session生成一个唯一的可以用于锁定的key
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // 对HandlerMethod进行参数等的适配处理,并调用目标handler
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            // 如果当前不存在session,则直接对HandlerMethod进行适配
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        // 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理,
    // 为其设置过期时间
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        // 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
        // 这里SessionAttribute主要是通过@SessionAttribute注解生成的
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        } else {
            // 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
            // 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
            // Cache的过期时间为-1,即立即失效
            prepareResponse(response);
        }
    }

    return mav;
}

提示:上面的程序作了两部分处理:1.判断当前是否对session进行同步处理,如果需要则进行加锁处理,不需要则直接调用。2.判断请求头是否已经包含Cache-Control请求头,如果不包含则设置Cache立即失效。对HandlerMethod的具体请求是在invokeHandlerMethod()方法中进行的。

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
        // 配置的InitBinder,用于进行参数的绑定
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
        // 中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 将handlerMethod封装为一个ServletInvocableHandlerMethod对象,
        // 该对象用于对当前request的整体调用流程进行了封装
        ServletInvocableHandlerMethod invocableMethod =
            createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            // 设置当前容器中配置的所有ArgumentResolver
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 设置当前容器中配置的所有ReturnValueHandler
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
        invocableMethod.setDataBinderFactory(binderFactory);
        // 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        // 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
        // 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
        // handler的返回值是否为WebAsyncTask或DefferredResult,如果是这两种中的一种,
        // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
        // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
        // 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,只有待目标任务
        // 完成之后才会回来将该异步任务的结果返回。
        AsyncWebRequest asyncWebRequest = WebAsyncUtils
            .createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        // 封装异步任务的线程池,request和interceptors到WebAsyncManager中
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        // 这里就是用于判断当前请求是否有异步任务结果的,如果存在,则对异步任务结果进行封装
        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) 
                asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            if (logger.isDebugEnabled()) {
                logger.debug("Found concurrent result value [" + result + "]");
            }
            // 封装异步任务的处理结果,虽然封装的是一个HandlerMethod,但只是Spring简单的封装
            // 的一个Callable对象,该对象中直接将调用结果返回了。这样封装的目的在于能够统一的
            // 进行右面的ServletInvocableHandlerMethod.invokeAndHandle()方法的调用
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
        // 还会判断是否需要将FlashAttributes封装到新的请求中
        return getModelAndView(mavContainer, modelFactory, webRequest);
    } finally {
        // 调用request destruction callbacks和对SessionAttributes进行处理
        webRequest.requestCompleted();
    }
}

提示:RequestMappingHandlerAdapter处理请求的主要流程:1.获取当前容器中使用@InitBinder注解注册的属性转换器。2.获取当前容器中使用@ModelAttribute标注但是没有使用@RequestMapping标注的方法,并且在调用目标方法之前调用这些方法。3.判断目标handler返回值是否使用了WebAsync Task或DefferredResult封装,如果封装了按照异步任务的方式执行。4.处理请求参数,调用目标方法和处理返回值。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) 
        throws Exception {
    // 判断当前缓存中是否缓存了当前bean所需要装配的InitBinder方法,如果存在,则直接从缓存中取,
    // 如果不存在,则在当前bean中进行扫描获取
    Class<?> handlerType = handlerMethod.getBeanType();
    Set<Method> methods = this.initBinderCache.get(handlerType);
    if (methods == null) {
        // 在当前bean中查找所有标注了@InitBinder注解的方法,这里INIT_BINDER_METHODS就是一个
        // 选择器,表示只获取使用@InitBinder标注的方法
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        this.initBinderCache.put(handlerType, methods);
    }

    // 这里initBinderAdviceCache是在RequestMappingHandlerAdapter初始化时同步初始化的,
    // 其内包含的方法有如下两个特点:①当前方法所在类使用@ControllerAdvice进行标注了;
    // ②当前方法使用@InitBinder进行了标注。也就是说其内保存的方法可以理解为是全局类型
    // 的参数绑定方法
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
    this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
        // 这里判断的是当前配置的全局类型的InitBinder是否能够应用于当前bean,
        // 判断的方式主要在@ControllerAdvice注解中进行了声明,包括通过包名,类所在的包,
        // 接口或者注解的形式限定的范围
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
        }
    });

    // 这里是将当前HandlerMethod所在bean中的InitBinder添加到需要执行的initBinderMethods中。
    // 这里从添加的顺序可以看出,全局类型的InitBinder会在当前bean中的InitBinder之前执行
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }

    // 将需要执行的InitBinder封装到InitBinderDataBinderFactory中
    return createDataBinderFactory(initBinderMethods);
}

13.1.2 RequestMappingHandlerAdapter

1.初始化过程

RequestMappingHandlerAdapter实现了InitializingBean接口,所以它的初始化入口是afterPropertiesSet方法。也就是说 RequestMappingHandlerAdapter 的创建是在 afterPropertiesSet() 方法中实现的。

提示:handler表示Spring处理具体请求的某个Controller方法,即HandlerAdapter表示将当前请求适配到某个Handler的处理器。RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理。

2.HandlerMapping接口的声明

public interface HandlerAdapter {
    // 用于判断当前HandlerAdapter是否能够处理当前请求
    boolean supports(Object handler);

    // 如果当前HandlerAdapter能够用于适配当前请求,那么就会处理当前请求中
    // 诸如参数和返回值等信息,以便能够直接委托给具体的Handler处理
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, 
        Object handler) throws Exception;

    // 获取当前请求的最后更改时间,主要用于供给浏览器判断当前请求是否修改过,
    // 从而判断是否可以直接使用之前缓存的结果
    long getLastModified(HttpServletRequest request, Object handler);
}

属性:1.argumentResolvers 用于给处理器方法和被@ModelAttribute注解的方法设置参数。2.initBinderArgumentResolvers 用于给被@InitBinder注解的方法设置参数。3.returnValueHandlers 用于将处理器返回值处理成ModelAndView类型。

  public void afterPropertiesSet() {

    //初始化注释了 @ControllerAdvice 的类的相关属性
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

13.2 RequestMappingHandlerAdapter 自身结构

13.2.1 创建RequestMappingHandlerAdapter

创建 RequestMappingHandlerAdapter 是在 afterPropertiesSet()方法中实现,内容主要是初始化 argumentResolvers | initBinderArgumentResolvers | returnValueHandlers | @ControllerAdvice注释的类相关的modelAttributeAdviceCache | initBinderAdviceCache | responseBodyAdvice 六个属性。

1.argumentResolvers 给处理器方法和注释了@ModelAttribute的方法设置参数
2.initBinderArgumentResolvers 给注释了@initBinder的方法设置参数
3.returnValueHandlers 将处理器的返回值处理成ModelAndView的类型
4.modelAttributeAdviceCache | initBinderAdviceCache 用于缓存 @ControllerAdvice注释的类里面的注释了@ModelAttribute 和 @InitBinder 的方法。
5.responseBodyAdvice 保存 ResponseBodyAdvice 接口,可以修改返回的 ResponseBody 的类。

注意:这些属性都是复数形式,可以有多个,使用的时候根据顺序调用即可。所以初始化时的顺序很重要。

13.2.2 RequestMappingHandlerAdapter之用

RequestMappingHandlerAdapter 的处理请求入口方法是handleInternal。

protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    //1.判断Handler是否有 @SessionAttribute 注释的参数
    if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    } else {
        this.checkAndPrepare(request, response, true);
    } 

    //看代码应该是从session中获取一些信息,然后初始化header等信息
    //这块就是根据需要是否进行同步操作
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized(mutex) {
                return this.invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
    //正式进入执行环节
    return this.invokeHandleMethod(request, response, handlerMethod);
}

提示:这里关键的方法就是:checkAndPrepare()方法和invokeHandleMethod()方法。

13.3 ModelAndViewContainer

ModelAndViewContainer承担着整个请求过程中数据的传递工作。除了保存Model和View之外还有其它的功能。它里面包含的属性有:

1.view 视图,Object类型
2.defaultModel:默认使用的Model
3.redirectModel:redirect类型的Model
4.sessionStatus:用于设置SessionAttribute使用完的标志
5.ignoreDefaultModelOnRedirect:如果为true则在处理器返回redirect视图时一定不使用defaultModel
6.redirectModelScenario:处理器返回redirect视图的标志
7.requestHandled:请求是否已经处理完成的标志

13.4 SessionAttributesHandler 和 SessionAttributeStore

SessionAttributesHandler 作为处理 @SessionAttributes注释的参数,其只做宏观的事情,如:哪个Handler都可以缓存哪些参数,某个参数在当前的SessionAttribute中是否存在等。具体的存储工作交给SessionAttributeStore做,

13.5 ModelFactory

ModelFactory是用来维护Model的。两个功能:1. 初始化Model。2.处理器执行后将Model中相应参数更新到SessionAttribute。

13.5.1 初始化Model

13.6 ServletInvocableHandlerMethod

是一种HandlerMethod,只是增加了方法执行的功能,相应地增加了参数解析、返回值处理等功能。

13.6.1 HandlerMethod

13.7 HandlerMethodArgumentResolver

是为处理器解析参数的,主要用在InvocableHandlerMethod中。每个Resolver对应一种类型的参数。

13.8 HandlerMethodReturnValueHandler

该处理方法用在ServletInvocableHandlerMethod中,功能:1. 将相应参数添加到Model。2. 设置View。3. 如果请求已经处理完则设置ModelAndViewContainer的requestHandled为true。

###

本文标题:HandlerAdapter

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:16:30

原始链接:http://bangjinhu.github.io/undefined/第13章 HandlerAdapter/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.